using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;

namespace Universe.Remoting.Extensions
{
    [Serializable]
    public class TrafficStatistic
    {
        [Serializable]
        public class Counters
        {
            public long Sent;
            public long Recieved;
            public long Requests;

            public static Counters Zero
            {
                get { return new Counters(); }
            }


            public Counters()
            {
            }


            public Counters(long sent, long recieved, long requests)
            {
                Sent = sent;
                Recieved = recieved;
                Requests = requests;
            }
        }



        object Sync = new object();
        public Counters Total = new Counters();

        Dictionary<string, Counters> ByMethods = new Dictionary<string, Counters>();
        Dictionary<string, Counters> ByClasses = new Dictionary<string, Counters>();

        public string[] GetClasses()
        {
            Dictionary<string, Counters> copy;
            lock (Sync)
            {
                copy = new Dictionary<string, Counters>(ByClasses);
            }

            return GetKeysSortedByCounters(copy).ToArray();
        }

        public string[] GetMethods()
        {
            Dictionary<string, Counters> copy;
            lock (Sync)
            {
                copy = new Dictionary<string, Counters>(ByMethods);
            }

            return GetKeysSortedByCounters(copy).ToArray();
        }

        public string[] GetMethods(string @class)
        {
            Dictionary<string, Counters> copyMethods;

            lock(Sync)
            {
                copyMethods = new Dictionary<string, Counters>(ByMethods);
            }

            Dictionary<string, Counters> myMethods = new Dictionary<string, Counters>();
            foreach (string method in copyMethods.Keys)
            {
                if (method.StartsWith(@class + "::"))
                    myMethods[method] = copyMethods[method];
            }

            return GetKeysSortedByCounters(myMethods).ToArray();
        }

        public void Clear()
        {
            lock(Sync)
            {
                Total.Requests = 0;
                Total.Recieved = 0;
                Total.Sent = 0;
                ByMethods.Clear();
                ByClasses.Clear();
            }
        }

        public void Add(string @class, string method, long sent, long recieved)
        {
            lock (Sync)
            {
                {
                    string fullMethod = @class + "::" + method;
                    Counters m;
                    if (!ByMethods.TryGetValue(fullMethod, out m))
                    {
                        m = new Counters();
                        m.Requests = 1;
                        m.Recieved = recieved;
                        m.Sent = sent;
                        ByMethods[fullMethod] = m;
                    }
                    else
                    {
                        m.Sent += sent;
                        m.Recieved += recieved;
                        m.Requests++;
                    }
                }

                {
                    Counters m;
                    if (!ByClasses.TryGetValue(@class, out m))
                    {
                        m = new Counters();
                        m.Requests = 1;
                        m.Recieved = recieved;
                        m.Sent = sent;
                        ByClasses[@class] = m;
                    }
                    else
                    {
                        m.Sent += sent;
                        m.Recieved += recieved;
                        m.Requests++;
                    }
                }
            }
        }

        static List<string> GetKeysSortedByCounters(Dictionary<string, Counters> dictionary)
        {
            List<string> list = new List<string>(dictionary.Keys);
            list.Sort(
                delegate(string x, string y)
                    {
                        Counters left = dictionary[x], right = dictionary[y];
                        long ret = left.Recieved - right.Recieved + left.Sent - right.Sent;
                        return -Math.Sign(ret);
                    });

            return list;
        }

        private TrafficStatistic Clone()
        {
            lock (Sync)
            {
                TrafficStatistic ret = new TrafficStatistic();
                ret.Total = new Counters(Total.Sent, Total.Recieved, Total.Requests);
                ret.ByClasses = new Dictionary<string, Counters>(ByClasses);
                ret.ByMethods = new Dictionary<string, Counters>(ByMethods);

                return ret;
            }
        }

        public String ToPlainReport()
        {
            TrafficStatistic my = Clone();
            StringBuilder ret = new StringBuilder();
            ret.AppendFormat("    Application: {0}", Assembly.GetEntryAssembly() != null ? Path.GetFileName(Assembly.GetEntryAssembly().Location) : "");
            ret.AppendLine();
            ret.AppendFormat("       Requests: {0:n0}", my.Total.Requests);
            ret.AppendLine();
            ret.AppendFormat("     Bytes sent: {0:n0}", my.Total.Sent);
            ret.AppendLine();
            ret.AppendFormat(" Bytes recieved: {0:n0}", my.Total.Recieved);
            ret.AppendLine();
            ret.AppendLine();

            List<string[]> raw = new List<string[]>();
            raw.Add(new string[] {"Class | Method", "Requests", "Bytes recieved", "Bytes sent"});

            raw.Add(new string[] { new string('-', raw[0][0].Length), new string('-', raw[0][1].Length), new string('-', raw[0][2].Length), new string('-', raw[0][3].Length) });

            string intent = "    ";
            string[] classes = my.GetClasses();
            foreach (string c in classes)
            {
                {
                    Counters v1 = my.ByClasses[c];
                    raw.Add(new string[] {c, v1.Requests.ToString("n0"), v1.Recieved.ToString("n0"), v1.Sent.ToString("n0")});
                }
                string[] methods = my.GetMethods(c);
                foreach (string method in methods)
                {
                    Counters v1 = my.ByMethods[method];
                    string m = method;
                    if (m.StartsWith(c + "::"))
                        m = m.Substring(c.Length + "::".Length);

                    raw.Add(new string[] {intent + m, v1.Requests.ToString("n0"), v1.Recieved.ToString("n0"), v1.Sent.ToString("n0")});
                }
            }

            int[] width = new int[4];
            for(int c=0; c<width.Length; c++)
            {
                width[c] = 0;
                foreach (string[] strings in raw)
                    width[c] = Math.Max(width[c], strings[c].Length);

            }

            for(int row = 0; row < raw.Count; row++)
            {
                for (int c = 0; c < width.Length; c++)
                {
                    string align = row == 0 || c == 0 ? "-" : "";
                    ret
                        .Append(c > 0 ? "    " : "")
                        .AppendFormat("{0," + align + width[c] + "}", raw[row][c]);
                }

                ret.AppendLine();
            }


            return ret.ToString();
        }

    }
}